package system

import (
	"encoding/json"
	"fmt"
	"hash/fnv"
	"log"
	"regexp"
	"server/config"
	"server/plugin/db"
	"strconv"
	"strings"
	"time"

	"github.com/redis/go-redis/v9"
)

// Movie 影片基本信息 (对应数据库movie表)
type Movie struct {
	Id          int64     `json:"id" gorm:"primaryKey;autoIncrement"` // 影片ID
	MovieID     int64     `json:"movie_id"`
	SourceID    string    `json:"source_id"`
	Name        string    `json:"name" gorm:"not null"`             // 影片名
	EnName      string    `json:"enName"`                           // 英文片名
	Cid         int64     `json:"cid" gorm:"not null"`              // 所属分类ID
	CName       string    `json:"cName"`                            // 所属分类名称
	Picture     string    `json:"picture"`                          // 海报图片
	Year        int       `json:"year"`                             // 年份
	Area        string    `json:"area"`                             // 地区
	State       string    `json:"state"`                            // 状态
	Remarks     string    `json:"remarks"`                          // 更新备注
	PlayFrom    string    `json:"playFrom"`                         // 主要播放来源
	Time        string    `json:"time"`                             // 更新时间
	Hits        int64     `json:"hits" gorm:"default:0"`            // 点击量
	IsHot       bool      `json:"isHot" gorm:"default:false"`       // 是否热门
	IsRecommend bool      `json:"isRecommend" gorm:"default:false"` // 是否推荐
	Status      bool      `json:"status" gorm:"default:true"`       // 状态
	SortOrder   int       `json:"sortOrder" gorm:"default:0"`       // 排序权重
	CreatedAt   time.Time `json:"createdAt"`                        // 创建时间
	UpdatedAt   time.Time `json:"updatedAt"`                        // 更新时间
}

// TableName 指定表名
func (Movie) TableName() string {
	return "movie"
}

// MovieDetailDB 影片详情信息 (对应数据库movie_detail表)
type MovieDetailDB struct {
	Id          int64     `json:"id" gorm:"primaryKey;autoIncrement"`  // 主键ID
	MovieId     int64     `json:"movieId" gorm:"uniqueIndex;not null"` // 影片ID
	Cid         int64     `json:"cid" gorm:"not null"`                 // 分类ID
	Pid         int64     `json:"pid" gorm:"not null"`                 // 一级分类ID
	Name        string    `json:"name" gorm:"not null"`                // 片名
	SubTitle    string    `json:"subTitle"`                            // 子标题
	CName       string    `json:"cName"`                               // 分类名称
	EnName      string    `json:"enName"`                              // 英文名
	Initial     string    `json:"initial"`                             // 首字母
	ClassTag    string    `json:"classTag"`                            // 分类标签
	Actor       string    `json:"actor"`                               // 主演
	Director    string    `json:"director"`                            // 导演
	Writer      string    `json:"writer"`                              // 编剧
	Blurb       string    `json:"blurb"`                               // 简介
	Content     string    `json:"content"`                             // 内容简介
	Remarks     string    `json:"remarks"`                             // 更新情况
	ReleaseDate string    `json:"releaseDate"`                         // 上映时间
	Area        string    `json:"area"`                                // 地区
	Language    string    `json:"language"`                            // 语言
	Year        string    `json:"year"`                                // 年份
	State       string    `json:"state"`                               // 影片状态
	UpdateTime  string    `json:"updateTime"`                          // 更新时间
	AddTime     int64     `json:"addTime"`                             // 资源添加时间戳
	DbId        int64     `json:"dbId"`                                // 豆瓣ID
	DbScore     string    `json:"dbScore"`                             // 豆瓣评分
	Hits        int64     `json:"hits" gorm:"default:0"`               // 影片热度
	Picture     string    `json:"picture"`                             // 简介图片
	PlayFrom    string    `json:"playFrom"`                            // 播放来源(JSON数组)
	DownFrom    string    `json:"downFrom"`                            // 下载来源
	CreatedAt   time.Time `json:"createdAt"`                           // 创建时间
	UpdatedAt   time.Time `json:"updatedAt"`                           // 更新时间
}

// TableName 指定表名
func (MovieDetailDB) TableName() string {
	return "movie_detail"
}

// MovieUrl 影片播放地址 (对应数据库movie_url表)
type MovieUrl struct {
	Id         int64     `json:"id" gorm:"primaryKey;autoIncrement"` // 主键ID
	MovieId    int64     `json:"movieId" gorm:"not null"`            // 影片ID
	SourceType int       `json:"sourceType" gorm:"default:1"`        // 资源类型(1:播放 2:下载)
	SourceName string    `json:"sourceName" gorm:"not null"`         // 播放源名称
	SiteId     string    `json:"siteId"`                             // 来源站点ID
	Episode    string    `json:"episode" gorm:"not null"`            // 集数/章节
	Link       string    `json:"link" gorm:"not null"`               // 播放/下载地址
	Quality    string    `json:"quality"`                            // 画质
	IsVip      bool      `json:"isVip" gorm:"default:false"`         // 是否需要VIP
	SortOrder  int       `json:"sortOrder" gorm:"default:0"`         // 排序
	Status     bool      `json:"status" gorm:"default:true"`         // 状态
	CreatedAt  time.Time `json:"createdAt"`                          // 创建时间
	UpdatedAt  time.Time `json:"updatedAt"`                          // 更新时间
}

// TableName 指定表名
func (MovieUrl) TableName() string {
	return "movie_url"
}

// MovieDescriptor 影片详情介绍信息
type MovieDescriptor struct {
	SubTitle    string `json:"subTitle"`    //子标题
	CName       string `json:"cName"`       //分类名称
	EnName      string `json:"enName"`      //英文名
	Initial     string `json:"initial"`     //首字母
	ClassTag    string `json:"classTag"`    //分类标签
	Actor       string `json:"actor"`       //主演
	Director    string `json:"director"`    //导演
	Writer      string `json:"writer"`      //作者
	Blurb       string `json:"blurb"`       //简介, 残缺,不建议使用
	Remarks     string `json:"remarks"`     // 更新情况
	ReleaseDate string `json:"releaseDate"` //上映时间
	Area        string `json:"area"`        // 地区
	Language    string `json:"language"`    //语言
	Year        string `json:"year"`        //年份
	State       string `json:"state"`       //影片状态 正片|预告...
	UpdateTime  string `json:"updateTime"`  //更新时间
	AddTime     int64  `json:"addTime"`     //资源添加时间戳
	DbId        int64  `json:"dbId"`        //豆瓣id
	DbScore     string `json:"dbScore"`     // 豆瓣评分
	Hits        int64  `json:"hits"`        //影片热度
	Content     string `json:"content"`     //内容简介
}

// MovieBasicInfo 影片基本信息
type MovieBasicInfo struct {
	Id       int64  `json:"id"`       //影片Id
	Cid      int64  `json:"cid"`      //分类ID
	Pid      int64  `json:"pid"`      //一级分类ID
	Name     string `json:"name"`     //片名
	SubTitle string `json:"subTitle"` //子标题
	CName    string `json:"cName"`    //分类名称
	State    string `json:"state"`    //影片状态 正片|预告...
	Picture  string `json:"picture"`  //简介图片
	Actor    string `json:"actor"`    //主演
	Director string `json:"director"` //导演
	Blurb    string `json:"blurb"`    //简介, 不完整
	Remarks  string `json:"remarks"`  // 更新情况
	Area     string `json:"area"`     // 地区
	Year     string `json:"year"`     //年份
}

// MovieUrlInfo 影视资源url信息
type MovieUrlInfo struct {
	Episode string `json:"episode"` // 集数
	Link    string `json:"link"`    // 播放地址
}

// MovieDetail 影片详情信息
type MovieDetail struct {
	Id       int64    `json:"id"`       //影片Id
	Cid      int64    `json:"cid"`      //分类ID
	Pid      int64    `json:"pid"`      //一级分类ID
	Name     string   `json:"name"`     //片名
	Picture  string   `json:"picture"`  //简介图片
	PlayFrom []string `json:"playFrom"` // 播放来源
	DownFrom string   `json:"DownFrom"` //下载来源 例: http
	//PlaySeparator   string              `json:"playSeparator"` // 播放信息分隔符
	PlayList        [][]MovieUrlInfo    `json:"playList"`     //播放地址url
	DownloadList    [][]MovieUrlInfo    `json:"downloadList"` // 下载url地址
	MovieDescriptor `json:"descriptor"` //影片描述信息
}

// ===================================Redis数据交互========================================================

// SaveDetails 保存影片详情信息到数据库和缓存
func SaveDetails(list []MovieDetail, sourceID string) (err error) {
	if len(list) == 0 {
		return nil
	}

	// 1. 准备批量保存的数据
	var movies []*Movie
	movieDetailsDB := make(map[int64]*MovieDetailDB)
	movieUrls := make(map[int64][]*MovieUrl)
	var infoList []SearchInfo

	// 2. 遍历list处理数据
	for _, detail := range list {
		// 序列化影片详情信息
		data, _ := json.Marshal(detail)
		// 保存到Redis缓存
		err = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieDetailKey, detail.Cid, detail.Id), data, config.FilmExpired).Err()
		if err != nil {
			log.Println("SaveDetails to Redis Error:", err)
			continue
		}

		// 同步保存简略信息到redis中
		SaveMovieBasicInfo(detail)

		// 转换并收集搜索信息
		searchInfo := ConvertSearchInfo(detail)
		infoList = append(infoList, searchInfo)
		// 保存 Search tag redis中
		SaveSearchTag(searchInfo)

		// 收集影片基本信息
		year, _ := strconv.Atoi(detail.Year)
		mv := &Movie{
			MovieID:     detail.Id,
			SourceID:    sourceID,
			Name:        detail.Name,
			EnName:      detail.EnName,
			Cid:         detail.Cid,
			CName:       detail.CName,
			Picture:     detail.Picture,
			Year:        year,
			Area:        detail.Area,
			State:       detail.State,
			Remarks:     detail.Remarks,
			PlayFrom:    detail.PlayFrom[0], // 取第一个播放来源
			Time:        detail.UpdateTime,
			Hits:        detail.Hits,
			IsHot:       false,
			IsRecommend: false,
			Status:      true,
			SortOrder:   0,
			CreatedAt:   time.Now(),
			UpdatedAt:   time.Now(),
		}
		movies = append(movies, mv)

		// 收集影片详情信息
		movieDetailsDB[detail.Id] = convertMovieDetailToDB(detail)

		// 收集播放地址信息
		for i, playSource := range detail.PlayList {
			sourceName := "默认播放源"
			if i < len(detail.PlayFrom) {
				sourceName = detail.PlayFrom[i]
			}

			for j, urlInfo := range playSource {
				movieUrl := &MovieUrl{
					MovieId:    detail.Id,
					SourceType: 1, // 播放类型
					SourceName: sourceName,
					Episode:    urlInfo.Episode,
					Link:       urlInfo.Link,
					SortOrder:  j,
					Status:     true,
				}
				movieUrls[detail.Id] = append(movieUrls[detail.Id], movieUrl)
			}
		}

		// 收集下载地址信息
		for i, downloadSource := range detail.DownloadList {
			for j, urlInfo := range downloadSource {
				movieUrl := &MovieUrl{
					MovieId:    detail.Id,
					SourceType: 2, // 下载类型
					SourceName: fmt.Sprintf("下载源%d", i+1),
					Episode:    urlInfo.Episode,
					Link:       urlInfo.Link,
					SortOrder:  j,
					Status:     true,
				}
				movieUrls[detail.Id] = append(movieUrls[detail.Id], movieUrl)
			}
		}
	}

	// 3. 批量保存到数据库
	// 3.1 保存影片基本信息
	if len(movies) > 0 {
		if err = db.Mdb.CreateInBatches(movies, 100).Error; err != nil {
			log.Println("SaveDetails: Batch save movies error:", err)
			return err
		}
	}
	// 将影片的ID赋值给影片详情信息
	for _, movie := range movies {
		movieDetailsDB[movie.MovieID].MovieId = movie.Id
		// 将影片Id 赋值给影片url实例
		for _, movieUrl := range movieUrls[movie.MovieID] {
			movieUrl.MovieId = movie.Id
		}
	}
	// 3.2 保存影片详情信息
	if len(movieDetailsDB) > 0 {
		var movieDetailsList []*MovieDetailDB
		for _, detail := range movieDetailsDB {
			movieDetailsList = append(movieDetailsList, detail)
		}
		if err = db.Mdb.CreateInBatches(movieDetailsList, 100).Error; err != nil {
			log.Println("SaveDetails: Batch save movie details error:", err)
			return err
		}
	}

	// 3.3 保存播放地址信息
	if len(movieUrls) > 0 {
		// 先删除旧的播放地址
		var movieIds []int64
		for _, detail := range list {
			movieIds = append(movieIds, detail.Id)
		}
		if err = db.Mdb.Where("movie_id IN ?", movieIds).Delete(&MovieUrl{}).Error; err != nil {
			log.Println("SaveDetails: Delete old movie urls error:", err)
			return err
		}

		// 将 map 转换为切片进行批量插入
		var allMovieUrls []*MovieUrl
		for _, urls := range movieUrls {
			allMovieUrls = append(allMovieUrls, urls...)
		}
		if err = db.Mdb.CreateInBatches(allMovieUrls, 100).Error; err != nil {
			log.Println("SaveDetails: Batch save movie urls error:", err)
			return err
		}
	}

	// 3.4 保存搜索信息到数据库
	// 确保search_info表存在
	// CreateSearchTable()
	// if err = BatchSaveSearchInfoToDB(infoList); err != nil {
	// 	log.Println("SaveDetails: Batch save search info error:", err)
	// 	return err
	// }

	// 3.5 将检索信息存入redis中做一次转存
	//RdbSaveSearchInfo(infoList)

	return nil
}

// SaveDetail 保存单部影片信息
func SaveDetail(detail MovieDetail) (err error) {
	// 序列化影片详情信息
	data, _ := json.Marshal(detail)
	// 保存影片信息到Redis
	err = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieDetailKey, detail.Cid, detail.Id), data, config.FilmExpired).Err()
	if err != nil {
		return err
	}
	// 2. 同步保存简略信息到redis中
	SaveMovieBasicInfo(detail)
	// 转换 detail信息
	searchInfo := ConvertSearchInfo(detail)
	// 3. 保存 Search tag redis中
	// 只存储用于检索对应影片的关键字信息
	SaveSearchTag(searchInfo)
	// 保存影片检索信息到searchTable
	err = SaveSearchInfo(searchInfo)
	return err
}

// SaveMovieBasicInfo 摘取影片的详情部分信息转存为影视基本信息
func SaveMovieBasicInfo(detail MovieDetail) {
	basicInfo := MovieBasicInfo{
		Id:       detail.Id,
		Cid:      detail.Cid,
		Pid:      detail.Pid,
		Name:     detail.Name,
		SubTitle: detail.SubTitle,
		CName:    detail.CName,
		State:    detail.State,
		Picture:  detail.Picture,
		Actor:    detail.Actor,
		Director: detail.Director,
		Blurb:    detail.Blurb,
		Remarks:  detail.Remarks,
		Area:     detail.Area,
		Year:     detail.Year,
	}
	data, _ := json.Marshal(basicInfo)
	_ = db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieBasicInfoKey, detail.Cid, detail.Id), data, config.FilmExpired).Err()
}

// SaveSitePlayList 保存播放url列表信息到数据库和缓存
func SaveSitePlayList(id string, list []MovieDetail) (err error) {
	// 如果list 为空则直接返回
	if len(list) <= 0 {
		return nil
	}

	// 1. 先保存到数据库
	var movieUrls []MovieUrl
	res := make(map[string]string)

	for _, d := range list {
		if len(d.PlayList) > 0 {
			// 不保存电影解说类
			if strings.Contains(d.CName, "解说") {
				continue
			}

			// 保存播放地址到数据库
			for i, playSource := range d.PlayList {
				for j, urlInfo := range playSource {
					movieUrl := MovieUrl{
						MovieId:    d.Id,
						SourceType: 1, // 播放类型
						SourceName: fmt.Sprintf("播放源%d", i+1),
						SiteId:     id,
						Episode:    urlInfo.Episode,
						Link:       urlInfo.Link,
						SortOrder:  j,
						Status:     true,
					}
					movieUrls = append(movieUrls, movieUrl)
				}
			}

			// 准备缓存数据
			data, _ := json.Marshal(d.PlayList[0])
			// 如果DbId不为0, 则以dbID作为key进行hash额外存储一次
			if d.DbId != 0 {
				res[GenerateHashKey(d.DbId)] = string(data)
			}
			res[GenerateHashKey(d.Name)] = string(data)
		}
	}

	// 批量保存到数据库
	if len(movieUrls) > 0 {
		if err = db.Mdb.CreateInBatches(movieUrls, 100).Error; err != nil {
			return err
		}
	}

	// 2. 保存到缓存
	if len(res) > 0 {
		// 保存形式 key: MultipleSource:siteName Hash[hash(movieName)]list
		err = db.Rdb.HMSet(db.Cxt, fmt.Sprintf(config.MultipleSiteDetail, id), res).Err()
	}
	return
}

// BatchSaveSearchInfo 批量保存Search信息到数据库和缓存
func BatchSaveSearchInfo(list []MovieDetail) {
	if len(list) == 0 {
		return
	}

	var infoList []SearchInfo
	for _, v := range list {
		infoList = append(infoList, ConvertSearchInfo(v))
	}

	// 1. 确保search_info表存在
	CreateSearchTable()

	// 2. 先保存到数据库 (使用改进的批量保存方法)
	if err := BatchSaveSearchInfoToDB(infoList); err != nil {
		log.Println("BatchSaveSearchInfo to DB Error:", err)
		return
	}

	// 3. 将检索信息存入redis中做一次转存
	RdbSaveSearchInfo(infoList)
}

// ConvertSearchInfo 将detail信息处理成 searchInfo
func ConvertSearchInfo(detail MovieDetail) SearchInfo {
	score, _ := strconv.ParseFloat(detail.DbScore, 64)
	stamp, _ := time.ParseInLocation(time.DateTime, detail.UpdateTime, time.Local)
	// detail中的年份信息并不准确, 因此采用 ReleaseDate中的年份
	year, err := strconv.ParseInt(regexp.MustCompile(`[1-9][0-9]{3}`).FindString(detail.ReleaseDate), 10, 64)
	if err != nil {
		year = 0
	}
	return SearchInfo{
		Mid:         detail.Id,
		Cid:         detail.Cid,
		Pid:         detail.Pid,
		Name:        detail.Name,
		SubTitle:    detail.SubTitle,
		CName:       detail.CName,
		ClassTag:    detail.ClassTag,
		Area:        detail.Area,
		Language:    detail.Language,
		Year:        year,
		Initial:     detail.Initial,
		Score:       score,
		Hits:        detail.Hits,
		UpdateStamp: stamp.Unix(),
		State:       detail.State,
		Remarks:     detail.Remarks,
		// ReleaseDate 部分影片缺失该参数, 所以使用添加时间作为上映时间排序
		ReleaseStamp: detail.AddTime,
	}
}

// GetBasicInfoByKey 获取Id对应的影片基本信息（缓存优先策略）
func GetBasicInfoByKey(key string) MovieBasicInfo {
	// 1. 先从缓存获取
	data := db.Rdb.Get(db.Cxt, key).Val()
	basic := MovieBasicInfo{}

	if data != "" {
		// 缓存中有数据，直接返回
		_ = json.Unmarshal([]byte(data), &basic)
		ReplaceBasicDetailPic(&basic)
		return basic
	}

	// 2. 缓存中没有，从数据库获取
	// 解析key获取cid和movieId: MovieBasicInfoKey格式为 "MovieBasicInfo:Cid%d:Id%d"
	var cid, movieId int64
	if _, err := fmt.Sscanf(key, config.MovieBasicInfoKey, &cid, &movieId); err != nil {
		return basic // 解析失败返回空
	}

	// 从数据库查询影片基本信息
	var movie Movie
	if err := db.Mdb.Where("id = ? AND cid = ?", movieId, cid).First(&movie).Error; err != nil {
		return basic // 数据库中没有数据
	}

	// 转换为MovieBasicInfo格式
	basic = MovieBasicInfo{
		Id:      movie.Id,
		Cid:     movie.Cid,
		Name:    movie.Name,
		CName:   movie.CName,
		State:   movie.State,
		Picture: movie.Picture,
		Remarks: movie.Remarks,
		Area:    movie.Area,
		Year:    fmt.Sprintf("%d", movie.Year),
	}

	// 3. 更新缓存
	basicData, _ := json.Marshal(basic)
	db.Rdb.Set(db.Cxt, key, basicData, config.FilmExpired)

	// 执行本地图片匹配
	ReplaceBasicDetailPic(&basic)
	return basic
}

// GetDetailByKey 获取影片对应的详情信息（缓存优先策略）
func GetDetailByKey(key string) MovieDetail {
	// 1. 先从缓存获取
	data := db.Rdb.Get(db.Cxt, key).Val()
	detail := MovieDetail{}

	if data != "" {
		// 缓存中有数据，直接返回
		_ = json.Unmarshal([]byte(data), &detail)
		ReplaceDetailPic(&detail)
		return detail
	}

	// 2. 缓存中没有，从数据库获取
	// 解析key获取cid和movieId: MovieDetailKey格式为 "MovieDetail:Cid%d:Id%d"
	var cid, movieId int64
	if _, err := fmt.Sscanf(key, config.MovieDetailKey, &cid, &movieId); err != nil {
		return detail // 解析失败返回空
	}

	// 从数据库查询影片详情信息
	var movieDetailDB MovieDetailDB
	if err := db.Mdb.Where("movie_id = ? AND cid = ?", movieId, cid).First(&movieDetailDB).Error; err != nil {
		return detail // 数据库中没有数据
	}

	// 查询播放地址
	var movieUrls []MovieUrl
	db.Mdb.Where("movie_id = ? AND status = ?", movieId, true).Order("source_name, sort_order").Find(&movieUrls)

	// 转换为MovieDetail格式
	detail = convertDBToMovieDetail(movieDetailDB, movieUrls)

	// 3. 更新缓存
	detailData, _ := json.Marshal(detail)
	db.Rdb.Set(db.Cxt, key, detailData, config.FilmExpired)

	// 执行本地图片匹配
	ReplaceDetailPic(&detail)
	return detail
}

// ==================== 数据库持久化操作 ====================

// convertDBToMovieDetail 将数据库结构转换为MovieDetail格式
func convertDBToMovieDetail(movieDetailDB MovieDetailDB, movieUrls []MovieUrl) MovieDetail {
	// 构建播放源列表
	playFromMap := make(map[string][]MovieUrlInfo)
	downloadFromMap := make(map[string][]MovieUrlInfo)

	for _, url := range movieUrls {
		urlInfo := MovieUrlInfo{
			Episode: url.Episode,
			Link:    url.Link,
		}

		if url.SourceType == 1 { // 播放类型
			playFromMap[url.SourceName] = append(playFromMap[url.SourceName], urlInfo)
		} else if url.SourceType == 2 { // 下载类型
			downloadFromMap[url.SourceName] = append(downloadFromMap[url.SourceName], urlInfo)
		}
	}

	// 转换播放源为数组格式
	var playFrom []string
	var playList [][]MovieUrlInfo
	for sourceName, urls := range playFromMap {
		playFrom = append(playFrom, sourceName)
		playList = append(playList, urls)
	}

	var downloadList [][]MovieUrlInfo
	for _, urls := range downloadFromMap {
		downloadList = append(downloadList, urls)
	}

	return MovieDetail{
		Id:           movieDetailDB.MovieId,
		Cid:          movieDetailDB.Cid,
		Pid:          movieDetailDB.Pid,
		Name:         movieDetailDB.Name,
		Picture:      movieDetailDB.Picture,
		PlayFrom:     playFrom,
		DownFrom:     movieDetailDB.DownFrom,
		PlayList:     playList,
		DownloadList: downloadList,
		MovieDescriptor: MovieDescriptor{
			SubTitle:    movieDetailDB.SubTitle,
			CName:       movieDetailDB.CName,
			EnName:      movieDetailDB.EnName,
			Initial:     movieDetailDB.Initial,
			ClassTag:    movieDetailDB.ClassTag,
			Actor:       movieDetailDB.Actor,
			Director:    movieDetailDB.Director,
			Writer:      movieDetailDB.Writer,
			Blurb:       movieDetailDB.Blurb,
			Remarks:     movieDetailDB.Remarks,
			ReleaseDate: movieDetailDB.ReleaseDate,
			Area:        movieDetailDB.Area,
			Language:    movieDetailDB.Language,
			Year:        movieDetailDB.Year,
			State:       movieDetailDB.State,
			UpdateTime:  movieDetailDB.UpdateTime,
			AddTime:     movieDetailDB.AddTime,
			DbId:        movieDetailDB.DbId,
			DbScore:     movieDetailDB.DbScore,
			Hits:        movieDetailDB.Hits,
			Content:     movieDetailDB.Content,
		},
	}
}

// SaveMovieWithCache 保存影片基本信息到数据库和缓存
func SaveMovieWithCache(movie *Movie) error {
	// 1. 保存到数据库
	if err := db.Mdb.Save(movie).Error; err != nil {
		return err
	}

	// 2. 清除相关缓存
	clearMovieCache(movie.Id, movie.Cid)
	return nil
}

// SaveMovieDetailWithCache 保存影片详情到数据库和缓存
func SaveMovieDetailWithCache(detail MovieDetail) error {
	// 1. 转换并保存到数据库
	movieDetailDB := convertMovieDetailToDB(detail)
	if err := db.Mdb.Save(&movieDetailDB).Error; err != nil {
		return err
	}

	// 2. 保存播放地址
	if err := saveMovieUrls(detail); err != nil {
		return err
	}

	// 3. 保存到缓存
	data, _ := json.Marshal(detail)
	db.Rdb.Set(db.Cxt, fmt.Sprintf(config.MovieDetailKey, detail.Cid, detail.Id), data, config.FilmExpired)

	// 4. 保存基本信息到缓存
	SaveMovieBasicInfo(detail)

	// 5. 保存搜索信息
	searchInfo := ConvertSearchInfo(detail)
	SaveSearchInfo(searchInfo)
	SaveSearchTag(searchInfo)

	return nil
}

// convertMovieDetailToDB 将MovieDetail转换为数据库格式
func convertMovieDetailToDB(detail MovieDetail) *MovieDetailDB {
	// 将播放源转换为JSON字符串
	playFromJSON, _ := json.Marshal(detail.PlayFrom)

	return &MovieDetailDB{
		MovieId:     detail.Id,
		Cid:         detail.Cid,
		Pid:         detail.Pid,
		Name:        detail.Name,
		SubTitle:    detail.SubTitle,
		CName:       detail.CName,
		EnName:      detail.EnName,
		Initial:     detail.Initial,
		ClassTag:    detail.ClassTag,
		Actor:       detail.Actor,
		Director:    detail.Director,
		Writer:      detail.Writer,
		Blurb:       detail.Blurb,
		Content:     detail.Content,
		Remarks:     detail.Remarks,
		ReleaseDate: detail.ReleaseDate,
		Area:        detail.Area,
		Language:    detail.Language,
		Year:        detail.Year,
		State:       detail.State,
		UpdateTime:  detail.UpdateTime,
		AddTime:     detail.AddTime,
		DbId:        detail.DbId,
		DbScore:     detail.DbScore,
		Hits:        detail.Hits,
		Picture:     detail.Picture,
		PlayFrom:    string(playFromJSON),
		DownFrom:    detail.DownFrom,
	}
}

// saveMovieUrls 保存影片播放地址到数据库
func saveMovieUrls(detail MovieDetail) error {
	// 先删除旧的播放地址
	db.Mdb.Where("movie_id = ?", detail.Id).Delete(&MovieUrl{})

	var movieUrls []MovieUrl

	// 保存播放地址
	for i, playSource := range detail.PlayList {
		sourceName := "默认播放源"
		if i < len(detail.PlayFrom) {
			sourceName = detail.PlayFrom[i]
		}

		for j, urlInfo := range playSource {
			movieUrl := MovieUrl{
				MovieId:    detail.Id,
				SourceType: 1, // 播放类型
				SourceName: sourceName,
				Episode:    urlInfo.Episode,
				Link:       urlInfo.Link,
				SortOrder:  j,
				Status:     true,
			}
			movieUrls = append(movieUrls, movieUrl)
		}
	}

	// 保存下载地址
	for i, downloadSource := range detail.DownloadList {
		for j, urlInfo := range downloadSource {
			movieUrl := MovieUrl{
				MovieId:    detail.Id,
				SourceType: 2, // 下载类型
				SourceName: fmt.Sprintf("下载源%d", i+1),
				Episode:    urlInfo.Episode,
				Link:       urlInfo.Link,
				SortOrder:  j,
				Status:     true,
			}
			movieUrls = append(movieUrls, movieUrl)
		}
	}

	// 批量保存
	if len(movieUrls) > 0 {
		return db.Mdb.CreateInBatches(movieUrls, 100).Error
	}

	return nil
}

// GetMovieByIdWithCache 根据ID获取影片信息（缓存优先）
func GetMovieByIdWithCache(id int64) (*Movie, error) {
	var movie Movie
	if err := db.Mdb.Where("id = ?", id).First(&movie).Error; err != nil {
		return nil, err
	}
	return &movie, nil
}

// GetMovieDetailByIdWithCache 根据ID获取影片详情（缓存优先）
func GetMovieDetailByIdWithCache(movieId, cid int64) MovieDetail {
	key := fmt.Sprintf(config.MovieDetailKey, cid, movieId)
	return GetDetailByKey(key)
}

// clearMovieCache 清除影片相关缓存
func clearMovieCache(movieId, cid int64) {
	// 清除详情缓存
	db.Rdb.Del(db.Cxt, fmt.Sprintf(config.MovieDetailKey, cid, movieId))
	// 清除基本信息缓存
	db.Rdb.Del(db.Cxt, fmt.Sprintf(config.MovieBasicInfoKey, cid, movieId))
}

// GetMultiplePlayWithCache 获取多站点播放源（缓存优先）
func GetMultiplePlayWithCache(siteId, key string) []MovieUrlInfo {
	// 1. 先从缓存获取
	data := db.Rdb.HGet(db.Cxt, fmt.Sprintf(config.MultipleSiteDetail, siteId), key).Val()
	var playList []MovieUrlInfo

	if data != "" {
		_ = json.Unmarshal([]byte(data), &playList)
		return playList
	}

	// 2. 缓存中没有，从数据库获取
	var movieUrls []MovieUrl
	db.Mdb.Where("site_id = ? AND status = ?", siteId, true).
		Order("source_name, sort_order").Find(&movieUrls)

	// 转换格式并更新缓存
	for _, url := range movieUrls {
		urlInfo := MovieUrlInfo{
			Episode: url.Episode,
			Link:    url.Link,
		}
		playList = append(playList, urlInfo)
	}

	// 更新缓存
	if len(playList) > 0 {
		cacheData, _ := json.Marshal(playList)
		db.Rdb.HSet(db.Cxt, fmt.Sprintf(config.MultipleSiteDetail, siteId), key, cacheData)
	}

	return playList
}

// BatchSaveSearchInfoToDB 批量保存搜索信息到数据库（改进版）
func BatchSaveSearchInfoToDB(infoList []SearchInfo) error {
	if len(infoList) == 0 {
		return nil
	}

	// 使用事务批量处理
	tx := db.Mdb.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()

	for _, info := range infoList {
		var count int64
		// 检查是否已存在
		if err := tx.Model(&SearchInfo{}).Where("mid = ?", info.Mid).Count(&count).Error; err != nil {
			log.Println("BatchSaveSearchInfoToDB Count Error:", err)
			tx.Rollback()
			return err
		}

		if count > 0 {
			// 更新现有记录
			updateData := map[string]interface{}{
				"update_stamp":  info.UpdateStamp,
				"hits":          info.Hits,
				"state":         info.State,
				"remarks":       info.Remarks,
				"score":         info.Score,
				"release_stamp": info.ReleaseStamp,
			}
			if err := tx.Model(&SearchInfo{}).Where("mid = ?", info.Mid).Updates(updateData).Error; err != nil {
				log.Println("BatchSaveSearchInfoToDB Update Error:", err)
				tx.Rollback()
				return err
			}
		} else {
			// 插入新记录
			if err := tx.Create(&info).Error; err != nil {
				log.Println("BatchSaveSearchInfoToDB Create Error:", err)
				tx.Rollback()
				return err
			}
			// 插入成功后保存tag信息到redis
			SaveSearchTag(info)
		}
	}

	// 提交事务
	if err := tx.Commit().Error; err != nil {
		log.Println("BatchSaveSearchInfoToDB Commit Error:", err)
		return err
	}

	log.Printf("BatchSaveSearchInfoToDB: Successfully processed %d records", len(infoList))
	return nil
}

// TestSaveSearchInfo 测试保存搜索信息功能
func TestSaveSearchInfo() error {
	// 1. 确保表存在
	CreateSearchTable()

	// 2. 创建测试数据
	testInfo := SearchInfo{
		Mid:          12345,
		Cid:          1,
		Pid:          1,
		Name:         "测试影片",
		SubTitle:     "测试副标题",
		CName:        "电影",
		ClassTag:     "动作,科幻",
		Area:         "美国",
		Language:     "英语",
		Year:         2024,
		Initial:      "C",
		Score:        8.5,
		UpdateStamp:  time.Now().Unix(),
		Hits:         1000,
		State:        "正片",
		Remarks:      "HD",
		ReleaseStamp: time.Now().Unix(),
	}

	// 3. 保存到数据库
	if err := SaveSearchInfo(testInfo); err != nil {
		log.Println("TestSaveSearchInfo Error:", err)
		return err
	}

	// 4. 验证数据是否保存成功
	var count int64
	if err := db.Mdb.Model(&SearchInfo{}).Where("mid = ?", testInfo.Mid).Count(&count).Error; err != nil {
		log.Println("TestSaveSearchInfo Count Error:", err)
		return err
	}

	if count > 0 {
		log.Printf("TestSaveSearchInfo: Success! Found %d record(s) in database", count)

		// 查询并显示保存的数据
		var savedInfo SearchInfo
		if err := db.Mdb.Where("mid = ?", testInfo.Mid).First(&savedInfo).Error; err != nil {
			log.Println("TestSaveSearchInfo Query Error:", err)
			return err
		}

		log.Printf("Saved SearchInfo: ID=%d, Name=%s, Mid=%d, Cid=%d",
			savedInfo.ID, savedInfo.Name, savedInfo.Mid, savedInfo.Cid)
		return nil
	} else {
		log.Println("TestSaveSearchInfo: Failed! No records found in database")
		return fmt.Errorf("no records found after save")
	}
}

// GetBasicInfoBySearchInfos 通过searchInfo 获取影片的基本信息
func GetBasicInfoBySearchInfos(infos ...SearchInfo) []MovieBasicInfo {
	var list []MovieBasicInfo
	for _, s := range infos {
		data := []byte(db.Rdb.Get(db.Cxt, fmt.Sprintf(config.MovieBasicInfoKey, s.Cid, s.Mid)).Val())
		basic := MovieBasicInfo{}
		_ = json.Unmarshal(data, &basic)

		// 执行本地图片匹配
		ReplaceBasicDetailPic(&basic)
		list = append(list, basic)
	}
	return list
}

/*
	对附属播放源入库时的name|dbID进行处理,保证唯一性
1. 去除name中的所有空格
2. 去除name中含有的别名～.*～
3. 去除name首尾的标点符号
4. 将处理完成后的name转化为hash值作为存储时的key
*/
// GenerateHashKey 存储播放源信息时对影片名称进行处理, 提高各站点间同一影片的匹配度
func GenerateHashKey[K string | ~int | int64](key K) string {
	mName := fmt.Sprint(key)
	//1. 去除name中的所有空格
	mName = regexp.MustCompile(`\s`).ReplaceAllString(mName, "")
	//2. 去除name中含有的别名～.*～
	mName = regexp.MustCompile(`～.*～$`).ReplaceAllString(mName, "")
	//3. 去除name首尾的标点符号
	mName = regexp.MustCompile(`^[[:punct:]]+|[[:punct:]]+$`).ReplaceAllString(mName, "")
	// 部分站点包含 动画版, 特殊别名 等字符, 需进行删除
	//mName = regexp.MustCompile(`动画版`).ReplaceAllString(mName, "")
	mName = regexp.MustCompile(`季.*`).ReplaceAllString(mName, "季")
	//4. 将处理完成后的name转化为hash值作为存储时的key
	h := fnv.New32a()
	_, err := h.Write([]byte(mName))
	if err != nil {
		return ""
	}
	return fmt.Sprint(h.Sum32())
}

// ============================采集方案.v1 遗留==================================================

// SaveMoves  保存影片分页请求list
func SaveMoves(list []Movie) (err error) {
	// 整合数据
	for _, m := range list {
		//score, _ := time.ParseInLocation(time.DateTime, m.Time, time.Local)
		movie, _ := json.Marshal(m)
		// 以Cid为目录为集合进行存储, 便于后续搜索, 以影片id为分值进行存储 例 MovieList:Cid%d
		err = db.Rdb.ZAdd(db.Cxt, fmt.Sprintf(config.MovieListInfoKey, m.Cid), redis.Z{Score: float64(m.Id), Member: movie}).Err()
	}
	return err
}

// AllMovieInfoKey 获取redis中所有的影视列表信息key MovieList:Cid
func AllMovieInfoKey() []string {
	return db.Rdb.Keys(db.Cxt, fmt.Sprint("MovieList:Cid*")).Val()
}

// GetMovieListByKey 获取指定分类的影片列表数据
func GetMovieListByKey(key string) []string {
	return db.Rdb.ZRange(db.Cxt, key, 0, -1).Val()
}
